package com.gitee.wsl.data.loader

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import com.gitee.wsl.data.cache.Cache
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlin.reflect.KClass

interface DataLoader<out R>{

     val fetchers: List<DataFetcher<Any,R>>

     companion object
}


inline fun <reified T : Any,reified D : Any,R> DataLoader<R>.loadResource(
    data: D,
    //dataKClass: KClass<*>,
    crossinline mapInput:(D) -> Any = { it },
    resourceConfig: ResourceConfig,
    crossinline decode:(R, ResourceConfig) -> T,
    cache: Cache<Any, T>?,
): Flow<ResourceResult<T>> = flow {
    val output = mapInput(data)
    //val output = data
    val cachedData = cache?.get(output)
    if (cachedData != null) {
        val resource = ResourceResult.Success(cachedData, DataSourceType.Memory)
        emit(resource)
    } else {
        val fetcher = findFetcherFor(output)
        //val decoder = findDecoderFor<T>()
        val bytesFlow = fetcher.fetch(output, resourceConfig)
        val dataFlow = bytesFlow.map { resource ->
            resource.map { channel ->
//                decoder.decode(channel, resourceConfig).also {
//                    cache[output] = it
//                }
                decode(channel, resourceConfig).also {
                   cache?.put(output, it)
              }
            }
        }
        emitAll(dataFlow)
    }
}.catch { emit(ResourceResult.Failure(it)) }

/**
 * Loads a cached [ImageBitmap], [ImageVector], or SVG [Painter] from memory. This includes mapping and loading the
 * cached image resource. If no resource has been cached for the provided data, `null` is returned.
 * @see Mapper
 * @see Cache
 */
fun <T : Any, R> DataLoader<R>.loadCachedResourceOrNull(
    data: Any,
    cache: Cache<Any, T>,
    dataKClass: KClass<*> = data::class,
): ResourceResult<T>? {
//    val output = mapInput(data, dataKClass)
//    return cache[output]?.let { Resource.Success(it, DataSource.Memory) }

    return cache[dataKClass]?.let { ResourceResult.Success(it, DataSourceType.Memory) }
}


fun <T : Any, R> DataLoader<R>.findFetcherFor(data: T): DataFetcher<T,R> {

    val type = data::class

    val fetcher = fetchers.findLast { fetcher ->

        val fetcherType = fetcher.inputDataKClass

        val isSameType = fetcherType == type

        isSameType && with(fetcher) { data.isSupported }
    }

    checkNotNull(fetcher) { "Unable to find a fetcher for $type" }

    return fetcher as DataFetcher<T,R>
}
